home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
magazine
/
pctchnqs
/
1990
/
number3
/
antivenm.asm
< prev
next >
Wrap
Assembly Source File
|
1990-04-15
|
12KB
|
291 lines
;***********************************************************************
; Listing 1 - ANTIVENM.ASM
;
; Written by Kevin D. Weeks 10-8-89
; Released to the Public Domain.
;
; int anti_venom(int buf_size,char* buffer)
;
; Description:
; This collection of routines performs a Cyclic Redundancy Check on
; the program they are a part of and compares the resulting value
; with a value known to be correct. A descrepancy in these two num-
; bers indicates the program has been modified and may be infected
; by a virus.
;
; Returns:
; -1 Program has been modified and may be infected.
; 0 No problems detected.
; +1 Execution error (file not found, PSP not found, etc.)
;
.MODEL small
if @codesize
bp_ofs equ 6
else
bp_ofs equ 4
endif
FOUND equ 0
EXECUTION_ERR equ 1
VIRUS_DETECTED equ -1
.DATA
state dw ? ; current state of valid_crc search
cur_crc dw ? ; current crc value
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
.CODE
public _anti_venom
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; The _anti_venom function performs the initial setup (such as ac-
; quiring the file name) and then successively calls READ_FILE and
; CALC_CRC until the entire file has been processed.
;
if @codesize ; if large code memory model
_anti_venom proc far
else ; else, small code memory model
_anti_venom proc near
endif
push bp ; save the caller's stack frame
mov bp,sp
push bx
push cx
push dx
push di
push si
push es
push ds
jmp STEP_1 ; jump over the CRC and signature
; the signature and validation crc are stored here to guarantee they'll
; be contiguous and not subject to compiler startup manipulation.
signature db 'ANTIV'
valid_crc dw 0
; the first step is to get the psp of this process.
STEP_1:
call GET_PSP ; returns with BX = PSP
cmp bx,0 ; check for a 0 return
je ERROR_OUT
; from the psp we get the address of the environment block which con-
; tains the path and name of this program.
call SCAN_ENVIRONMENT
; SCAN_ENVIRONMENT returns with DS:DX pointing to file path and name
; ready for a call to DOS to open the file.
mov ax,3d00h ; Open File function - read only
int 21h ; call DOS
jc ERROR_OUT
; get the buffer address form the stack. NOTE: this is language specific!
mov bx,ax ; move file handle to bx
mov cx,[bp + bp_ofs] ; get buffer size
mov dx,[bp + bp_ofs + 2] ; get offset address of buffer
if @datasize ; if large data model
mov ds,[bp + bp_ofs + 4] ; get segment address of buffer
else ; else small data model
pop ds ; restore original data segment
push ds ; keep stack accurate
endif
call CLEAR_OUT ; initialize the state
; this is where the real work gets done. we read to the end of the file
; calling CALC_CRC after each read.
MAIN_LOOP:
call READ_FILE ; read the file
jc ERROR_OUT ; bailout if read error
cmp ax,0 ; check for EOF
jz COMPARE_CRC ; if so, complete testing
; else
push cx ; save buffer size
mov cx,ax ; place bytes actually read in cx
call CALC_CRC ; calculate CRC
pop cx ; restore buffer size
jmp MAIN_LOOP ; and repeat
; end of MAIN_LOOP
; test the crc just calculated by subtracting it from the valid crc. if
; the result is 0 then we have our return code, otherwise we indicate a
; virus was detected
COMPARE_CRC:
mov ax,cs:valid_crc ; move the correct crc to ax
sub ax,cs:cur_crc ; and subtract the new crc
jz CLOSE ; if the result is 0 they're equal
; else
mov ax,VIRUS_DETECTED ; indicate crc error
jmp short CLOSE ; and bailout
ERROR_OUT:
mov ax,EXECUTION_ERR ; indicate execution error
CLOSE:
push ax ; save the return code
mov ax,3e00h ; Close File function
int 21h ; call DOS
pop ax ; restore the return code
EXIT:
pop ds ; restore the caller's environment
pop es
pop si
pop di
pop dx
pop cx
pop bx
mov sp,bp
pop bp
ret
_anti_venom endp
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; GET_PSP
; First we issue a call to DOS function 62h, Get PSP. this function only
; exists in DOS 3.xx but IS documented. If we get a 0 back in register bx
; then we assume we're running under DOS 2.xx and issue a call to the UN-
; documented DOS function 51h.
;
GET_PSP proc near
mov ax,6200h ; DOS Get PSP function
int 21h ; call DOS
cmp bx,0 ; check for a 0 return
jz GET_PSP2
ret ; if not, return
GET_PSP2:
mov ax,5100h ; undocumented Get PSP function
int 21h ; call DOS
ret
GET_PSP endp
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; SCAN_ENVIRONMENT
; The DOS environment blocks consist of a series of ASCIIz strings. The
; last string is double terminated, followed by the number of additional
; strings(?), followed by the ASCIIz path and filename of the current
; process, which is what we want.
;
SCAN_ENVIRONMENT proc near
mov si,002ch ; offset of pointer to environment block
mov es,bx ; move psp segment into es
mov ds,es:[si] ; move environment segment into ds
mov si,0 ; set index to beginning
mov cx,0 ; use cx to count NUL-terminators
SCAN_0:
cmp byte ptr [si],0 ; check for a zero
jnz SCAN_1 ; no zero so check next character
; else
inc cx ; increment cx
inc si ; point to next character
cmp cx,2 ; check to see if we've got two 0's
je SCAN_2 ; we do so exit loop
; else
jmp SCAN_0 ; loop again
SCAN_1:
inc si ; point to next character
mov cx,0 ; re-initialize cx
jmp SCAN_0 ; and loop again
SCAN_2: ; we've found the double NUL
inc si ; skip over the string count
inc si
mov dx,si ; ds:dx = pointer to this program's
; path and name
ret ; return
SCAN_ENVIRONMENT endp
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; READ_FILE
;
READ_FILE proc near
mov ax,3f00h ; Read File function
int 21h ; call DOS
ret ; return immediately
READ_FILE endp
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; CALC_CRC
; This routine consists of two loops. The outer one processes the input
; buffer byte-by-byte while the inner loop calculates the crc on each
; byte.
;
CALC_CRC proc near
mov si,dx ; move pointer to buffer to si
FOR_1:
push cx ; save buffer count
mov ah,[si] ; move character to ah
mov al,0 ; clear low byte of ax
xor ax,cs:cur_crc ; combine character with crc
mov cx,8 ; set cx to 8 for 'for' loop
FOR_2:
test ax,8000h ; see if MSB is on
jz CCRC_2 ; if not, perform else
shl ax,1 ; shift left one
xor ax,1021h ; and incorporate prime
loop FOR_2 ; loop back
jmp short CCRC_3 ; we finished the crc calculation
CCRC_2: ; else
shl ax,1 ; shift left one
loop FOR_2 ; and loop
CCRC_3:
pop cx ; restore buffer length to cx
cmp cs:state,FOUND ; see if we've found the signature
je END_OF_FOR2 ; if so, go on
call FIND_VALID_CRC ; else, check this character
END_OF_FOR2:
inc si ; point to next byte
mov cs:cur_crc,ax ; save current crc
loop FOR_1 ; and loop
ret
CALC_CRC endp
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; FIND_VALID_CRC
; This is implemented as a finite state machine (I use these things ALL
; the time) where each state is represented by the address of the state
; processor. This would of course have to be modified for a different
; signature. Alternatively, it could be handled the way SETCRC.C does it
; which is more general but less clear. My feeling is that since you
; should change the signature for each program you use this in you might
; as well change this routine also. It's simple enough to do.
;
FIND_VALID_CRC proc near
jmp cs:state
FIND_A:
cmp byte ptr [si],'A' ; if target value not seen
jne CLEAR_OUT ; go back to beginning
mov cs:state,offset FIND_N ; else, change state
ret
FIND_N:
cmp byte ptr [si],'N'
jne CLEAR_OUT
mov cs:state,offset FIND_T
ret
FIND_T:
cmp byte ptr [si],'T'
jne CLEAR_OUT
mov cs:state,offset FIND_I
ret
FIND_I:
cmp byte ptr [si],'I'
jne CLEAR_OUT
mov cs:state,offset FIND_V
ret
FIND_V:
cmp byte ptr [si],'V'
jne CLEAR_OUT
mov cs:state,FOUND ; we've found the signature
dec cx ; so increment the byte counter
dec cx ; past it
inc si ; and increment the pointer
inc si ; past it
ret
CLEAR_OUT:
mov cs:state,offset FIND_A
ret
FIND_VALID_CRC endp
END